import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisStopException;
import com.alibaba.excel.metadata.data.ReadCellData;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.File;
import java.util.*;

public class Main extends Application {

    /**
     * 每页条数
     */
    private static int pageSize = 100;
    /**
     * 表格
     */
    private TableView<List<String>> tableView;
    /**
     * 表头
     */
    private List<String> header;
    /**
     * 列表数据
     */
    private ObservableList<List<String>> data;
    /**
     * 当前数据，0开始
     */
    private int currentPage = 0;
    /**
     * 是否初始化表头
     */
    private boolean initHeader = false;
    /**
     * 当前打开文件
     */
    private File currentFile;
    /**
     * 页数输入框
     */
    private TextField pageInput;

    private ComboBox<String> columnComboBox;
    private ComboBox<CheckBox> filterComboBox;
    private ObservableList<CheckBox> filterOptions;
    private Set<String> selectedFilters;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("page-excel");

        //表格渲染
        tableView = new TableView<>();
        tableView.setEditable(true);
        data = FXCollections.observableArrayList();
        tableView.setItems(data);
        tableView.setMinHeight(800);
        tableView.setMaxHeight(800);

        Button openButton = new Button("打开文件");
        openButton.setOnAction(e -> {
            openFile(primaryStage);
        });

        Button prevButton = new Button("上一页");
        prevButton.setOnAction(e -> loadPage(currentPage - 1));

        Button nextButton = new Button("下一页");
        nextButton.setOnAction(e -> loadPage(currentPage + 1));

        //页面跳转
        pageInput = new TextField();
        pageInput.setPromptText("输入页码");

        Button goToPageButton = new Button("跳转页码");
        goToPageButton.setOnAction(e -> {
            int page = Integer.parseInt(pageInput.getText());
            loadPage(page);
        });

        //设置每页条数
        TextField itemsPerPageInput = new TextField();
        itemsPerPageInput.setPromptText("每页条数");
        itemsPerPageInput.setText(String.valueOf(pageSize));  // 设置默认值

        Button setItemsPerPageButton = new Button("设置每页条数");
        setItemsPerPageButton.setOnAction(e -> {
            try {
                pageSize = Integer.parseInt(itemsPerPageInput.getText());
                loadPage(0);  // 重新加载第一页
            } catch (NumberFormatException ex) {
                Alert alert = new Alert(Alert.AlertType.WARNING, "请输入有效的条数！");
                alert.show();
            }
        });

        columnComboBox = new ComboBox<>();
        columnComboBox.setPromptText("选择列");
        columnComboBox.setOnAction(e -> updateFilterOptions());

        filterOptions = FXCollections.observableArrayList();
        filterComboBox = new ComboBox<>(filterOptions);
        filterComboBox.setPromptText("选择筛选条件");
        filterComboBox.setVisibleRowCount(10);
        selectedFilters = new HashSet<>();

        Button applyFilterButton = new Button("应用筛选");
        applyFilterButton.setOnAction(e -> {
            selectedFilters.clear();
            for (CheckBox checkBox : filterOptions) {
                boolean selected = checkBox.isSelected();
                String text = checkBox.getText();
                if (selected) {
                    selectedFilters.add(text);
                }
            }
            loadPage(0);
        });

        //布局
        HBox hBox = new HBox(openButton, prevButton, nextButton
                , pageInput, goToPageButton
                , itemsPerPageInput, setItemsPerPageButton
                , columnComboBox, filterComboBox, applyFilterButton
        );
        VBox vbox = new VBox(hBox, tableView);
        Scene scene = new Scene(vbox, 1300, 900);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void openFile(Stage stage) {
        FileChooser fileChooser = new FileChooser();
        fileChooser.getExtensionFilters().addAll(
                new FileChooser.ExtensionFilter("Excel Files", "*.xlsx")
        );
        currentFile = fileChooser.showOpenDialog(stage);
        initHeader = false;
        if (currentFile != null) {
            currentPage = 0;
            loadPage(0);
        }
    }

    /**
     * 加载页面
     * @param page
     */
    private void loadPage(int page) {
        if (currentFile == null || page < 0) {
            return;
        }

        int fromRow = page * pageSize;
        List<List<String>> pageData = readExcelPage(currentFile, fromRow, pageSize);

        if (pageData.isEmpty() && page > 0) {
            return; // No more data to load
        }

        data.clear(); // Clear existing data
        data.addAll(pageData);

        currentPage = page;
        pageInput.setText(String.valueOf(page+1));
    }

    /**
     * 设置表头
     * @param header
     */
    private void setupTableColumns(List<String> header) {
        tableView.getColumns().clear();
        for (int i = 0; i < header.size(); i++) {
            TableColumn<List<String>, String> column = new TableColumn<>(header.get(i));
            final int colIndex = i;
            column.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().get(colIndex)));
            column.setCellFactory(c -> new SelectableTextCell<>());
            tableView.getColumns().add(column);
        }

        columnComboBox.getItems().clear();
        columnComboBox.getItems().addAll(header);
    }

    /**
     * 读excel
     * @param file
     * @param fromRow
     * @param pageSize
     * @return
     */
    private List<List<String>> readExcelPage(File file, int fromRow, int pageSize) {
        List<Map<Integer, String>> rawData = new ArrayList<>();
        EasyExcel.read(file, new AnalysisEventListener<Map<Integer, String>>() {
            private int currentRow = 0;

            @Override
            public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                super.invokeHead(headMap, context);
                if (initHeader) {
                    return;
                }
                List<String> header2 = new ArrayList<>();
                for (Map.Entry<Integer, ReadCellData<?>> entry : headMap.entrySet()) {
                    header2.add(entry.getValue().getStringValue());
                }
                header = header2;
                setupTableColumns(header);
                initHeader = true;
            }

            @Override
            public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
                if (currentRow >= fromRow && rawData.size() < pageSize) {
                    boolean isAdd = isAdd(rowData);
                    if (isAdd) {
                        rawData.add(rowData);
                    }
                }
                currentRow++;
                if (rawData.size() >= pageSize) {
                    throw new ExcelAnalysisStopException(); // Stop reading more rows
                }
            }

            private boolean isAdd(Map<Integer, String> rowData) {
                if (selectedFilters.isEmpty()) {
                    return true;
                }
                int selectedIndex = columnComboBox.getSelectionModel().getSelectedIndex();
                String value = rowData.get(selectedIndex);
                boolean isAdd = selectedFilters.contains(value);
                return isAdd;
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                // No-op
            }
        }).sheet().headRowNumber(1).doRead();

        return convertRawDataToList(rawData);
    }

    private List<List<String>> convertRawDataToList(List<Map<Integer, String>> rawData) {
        List<List<String>> data = new ArrayList<>();
        for (Map<Integer, String> row : rawData) {
            List<String> rowData = new ArrayList<>();
            for (int i = 0; i < row.size(); i++) {
                rowData.add(row.getOrDefault(i, ""));
            }
            data.add(rowData);
        }
        return data;
    }

    private void updateFilterOptions() {
        if (currentFile == null || columnComboBox.getSelectionModel().isEmpty()) {
            return;
        }

        int columnIndex = columnComboBox.getSelectionModel().getSelectedIndex();
        Set<String> uniqueValues = new HashSet<>();

        EasyExcel.read(currentFile, new AnalysisEventListener<Map<Integer, String>>() {
            @Override
            public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
            }

            @Override
            public void invoke(Map<Integer, String> rowData, AnalysisContext context) {
                if (uniqueValues.size() > 100) {
                    return;
                }
                uniqueValues.add(rowData.get(columnIndex));
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
            }
        }).sheet().headRowNumber(1).doRead();

        filterOptions.clear();
        for (String value : uniqueValues) {
            filterOptions.add(new CheckBox(value));
        }
    }
}